package ansible

import (
	"context"
	"io"
	"os"
	"path"
)

// Envs 定义环境变量类型
type Envs = map[string]string

// Playbook 表示 Ansible Playbook 执行器
type Playbook struct {
	config    *Config
	inventory interface{}
	args      []string
	extraVars map[string]string
	tags      string
	executor  Executor
}

// NewPlaybook 创建新的 Playbook 实例
func NewPlaybook(binPath string, inventory interface{}, ctx context.Context) *Playbook {
	if binPath == "" {
		binPath = "ansible-playbook"
	}
	if ctx == nil {
		ctx = context.Background()
	}

	config := DefaultPlaybookConfig().
		WithBinPath(binPath).
		WithContext(ctx)

	return &Playbook{
		config:    config,
		inventory: inventory,
		extraVars: make(map[string]string),
		args:      make([]string, 0),
		executor:  NewDefaultExecutor(),
	}
}

// WorkDirectory 设置工作目录
func (ap *Playbook) WorkDirectory(dir string) *Playbook {
	ap.config.WorkDirectory = dir
	return ap
}

// RequestPty 启用 PTY 模式
func (ap *Playbook) RequestPty() *Playbook {
	ap.config.EnablePTY = true
	return ap
}

// SetArgs 设置额外的命令行参数
func (ap *Playbook) SetArgs(args ...string) *Playbook {
	ap.args = append(ap.args, args...)
	return ap
}

// SetEnv 设置环境变量
func (ap *Playbook) SetEnv(k, v string) *Playbook {
	ap.config.SetEnv(k, v)
	return ap
}

// SetEnvs 批量设置环境变量
func (ap *Playbook) SetEnvs(envs Envs) *Playbook {
	ap.config.SetEnvs(envs)
	return ap
}

// SetAdditionalVar 设置额外变量
func (ap *Playbook) SetAdditionalVar(k, v string) *Playbook {
	if ap.extraVars == nil {
		ap.extraVars = make(map[string]string)
	}
	ap.extraVars[k] = v
	return ap
}

// SetAdditionalVars 批量设置额外变量
func (ap *Playbook) SetAdditionalVars(vars map[string]string) *Playbook {
	for k, v := range vars {
		ap.SetAdditionalVar(k, v)
	}
	return ap
}

// SetTags 设置标签过滤
func (ap *Playbook) SetTags(tags string) *Playbook {
	ap.tags = tags
	return ap
}

// SetOutJson 设置 JSON 输出格式
func (ap *Playbook) SetOutJson() *Playbook {
	ap.config.WithOutputFormat(OutputFormatJSON)
	return ap
}

// SetOutTimer 设置 Timer 输出格式
func (ap *Playbook) SetOutTimer() *Playbook {
	ap.config.WithOutputFormat(OutputFormatTimer)
	return ap
}

// ForceColor 强制启用彩色输出
func (ap *Playbook) ForceColor() *Playbook {
	ap.config.WithForceColor(true)
	return ap
}

// CloseWarnings 关闭警告信息
func (ap *Playbook) CloseWarnings() *Playbook {
	ap.config.WithSuppressWarnings(true)
	return ap
}

// SetStdout 设置标准输出
func (ap *Playbook) SetStdout(w io.Writer) *Playbook {
	if executor, ok := ap.executor.(*DefaultExecutor); ok {
		executor.WithStdout(w)
	}
	return ap
}

// SetStderr 设置标准错误输出
func (ap *Playbook) SetStderr(w io.Writer) *Playbook {
	if executor, ok := ap.executor.(*DefaultExecutor); ok {
		executor.WithStderr(w)
	}
	return ap
}

// SetStdin 设置标准输入
func (ap *Playbook) SetStdin(r io.Reader) *Playbook {
	if executor, ok := ap.executor.(*DefaultExecutor); ok {
		executor.WithStdin(r)
	}
	return ap
}

// RunWithFile 执行指定的 Playbook 文件
func (ap *Playbook) RunWithFile(playbookFile string) ([]byte, []byte, error) {
	// 检查文件是否存在
	var fullPath string
	if path.IsAbs(playbookFile) {
		fullPath = playbookFile
	} else {
		fullPath = path.Join(ap.config.WorkDirectory, playbookFile)
	}

	if _, err := os.Stat(fullPath); err != nil {
		return nil, nil, NewAnsibleError("file_check", playbookFile, err, nil, nil)
	}

	// 构建命令参数
	args := append([]string{}, ap.args...)

	// 处理 inventory
	switch inv := ap.inventory.(type) {
	case string:
		if inv != "" {
			args = append(args, "-i", inv)
		}
	case *Inventory:
		tempFile, err := inv.LocalStorage()
		if err != nil {
			return nil, nil, NewAnsibleError("inventory", "create_temp", err, nil, nil)
		}
		defer inv.Close()
		args = append(args, "-i", tempFile)
	case nil:
		// 使用默认 inventory
	default:
		return nil, nil, ErrInvalidInventory
	}

	// 添加 playbook 文件
	args = append(args, fullPath)

	// 添加额外变量
	for k, v := range ap.extraVars {
		args = append(args, "-e", k+"="+v)
	}

	// 添加标签过滤
	if ap.tags != "" {
		args = append(args, "-t", ap.tags)
	}

	// 执行命令
	result, err := ap.executor.Execute(ap.config.Context, ap.config, args)
	if err != nil {
		return result.Stdout, result.Stderr, err
	}

	return result.Stdout, result.Stderr, nil
}

// RunWithBytes 执行字节数组形式的 Playbook
func (ap *Playbook) RunWithBytes(playbook []byte) ([]byte, []byte, error) {
	tempF, err := os.CreateTemp("", "ansible_playbook_*.yaml")
	if err != nil {
		return nil, nil, NewAnsibleError("temp_file", "create", err, nil, nil)
	}
	defer os.Remove(tempF.Name())
	defer tempF.Close()

	_, err = tempF.Write(playbook)
	if err != nil {
		return nil, nil, NewAnsibleError("temp_file", "write", err, nil, nil)
	}

	return ap.RunWithFile(tempF.Name())
}

// RunWithFileParsed 执行 Playbook 文件并解析结果
func (ap *Playbook) RunWithFileParsed(playbookFile string) (*PlaybookResult, error) {
	stdout, stderr, err := ap.RunWithFile(playbookFile)
	if err != nil {
		// 即使有错误，也尝试解析结果
		parser := NewResultParser(ap.config.OutputFormat)
		if result, parseErr := parser.ParsePlaybookResult(stdout, stderr); parseErr == nil {
			return result, err
		}
		return nil, err
	}

	parser := NewResultParser(ap.config.OutputFormat)
	return parser.ParsePlaybookResult(stdout, stderr)
}

// RunWithBytesParsed 执行字节数组形式的 Playbook 并解析结果
func (ap *Playbook) RunWithBytesParsed(playbook []byte) (*PlaybookResult, error) {
	stdout, stderr, err := ap.RunWithBytes(playbook)
	if err != nil {
		// 即使有错误，也尝试解析结果
		parser := NewResultParser(ap.config.OutputFormat)
		if result, parseErr := parser.ParsePlaybookResult(stdout, stderr); parseErr == nil {
			return result, err
		}
		return nil, err
	}

	parser := NewResultParser(ap.config.OutputFormat)
	return parser.ParsePlaybookResult(stdout, stderr)
}
